//路由守卫
//路由守卫鉴权，单独引出一个文件来写，方便维护. 该文件需要在main.js里面引入，让其生效
import router from './router'
import store from './store'
import AsyncRoute from './router/AsyncRoute'

// 访问白名单
const whiteList = ['/login', '/404']
// 知识点：路由守卫，to代表访问的页面，from代表当前页面，next可以用于放行请求，也可以用于页面跳转
router.beforeEach(async (to, from, next) => {
  if (store.state.user.token) {
    /**
     * next()放行导航
     *  知识点：【重点】
     * 1.next()尝试写在最前面，它的后面写代码逻辑的地方，否则可能造成【路由冲突报错】
     * 2.可以写在处理逻辑最后，但要可能要使用next({ ...to, replace: true })，如本页最后就是用了它。但它也可能触发另外一个报错，
     * 有几种方法处理该报错：
     *  2.1 降低vue-router的版本，3以下？尽量低一点的版本
     *  2.2 在导致报错的push跳转路由的后面加上catch,忽略这个错误
     *  2.3 这个报错本质是一个提示，不影响功能，开发上线不会出现，也可以不处理
     *
     * 【路由冲突报错】我的理解：当router.addRoute添加动态路由，也就是当前的路由配置被修改，Vue Router 会认为
     * 「最初启动的那个导航已经失效」，从而主动取消它，此时调用next() 抛出 Navigation cancelled 错误
     */
    // next()

    //有token 再次访问登录页默认 跳转到主页（不用重复访问登录页）
    if (to.path === '/login') {
      next('/')
    } else {
      //判断用户访问权限,  知识点：获取权限信息的逻辑放到路由守卫里
      //知识点：【重点】调用方法的时候必须要确认是不是异步方法，异步方法的话加await
      // 知识点：hasAddedRoutes 动态路由标记，路由只需要添加一次即可，避免每次跳转都重复添加路由信息
      if (!store.state.user.hasAddedRoutes) {
        const permissions = await store.dispatch('user/getPermissions')
        //store.state.user.permissions

        // console.log(permissions)
        //获取用户权限（一级路由访问权限）
        const firstRoutes = getFirstRoutes(permissions)
        //获取用户权限（二级路由访问权限）
        const secondRoutes = getSecondRoutes(permissions)

        //筛选用户可访问的路由
        const visitableRoutes = getVisitableRoutes(
          AsyncRoute,
          firstRoutes,
          secondRoutes
        )

        //知识点：【权限控制】 把筛选后的路由动态添加给router对象（添加之后，router才可以实现动态路由跳转）
        visitableRoutes.forEach(route => router.addRoute(route))
        // 菜单动态获取并展示
        store.commit('user/updateMenuList', visitableRoutes)

        console.log('全部动态路由：')
        console.log(AsyncRoute)
        console.log('用户权限：')
        console.log(permissions)
        console.log('一级路由权限：')
        console.log(firstRoutes)
        console.log('二级路由权限：')
        console.log(secondRoutes)
        console.log('可访问路由：')
        console.dir(visitableRoutes)

        store.commit('user/setHasAddedRoutes', true) // 新增：标记路由已添加
        //知识点：新建对象，且修改原对象的某些属性。如下只修改replace属性，其他不变
        next({ ...to, replace: true })
      } else {
        next()
      }
    }
  } else {
    // 没有token的情况
    if (whiteList.includes(to.path)) {
      next()
    } else {
      next('/login')
    }
  }
})

// 获取一级路由
const getFirstRoutes = permissions => {
  // 知识点： 伪数组需要转为数组才可以使用map方法
  const res = Array.from(permissions).map(item => {
    return item.split(':')[0]
  })
  // set去重
  return [...new Set(res)]
}

const getSecondRoutes = permissions => {
  const res = Array.from(permissions).map(item => {
    return item.split(':')[0] + ':' + item.split(':')[1]
  })
  return [...new Set(res)]
}

// 获取当前可访问路由
const getVisitableRoutes = (AsyncRoute, firstRoutes, secondRoutes) => {
  //超级管理员的情况,返回所有动态路由
  if (firstRoutes.includes('*')) {
    return AsyncRoute
  }

  const res = AsyncRoute.filter(item => {
    return firstRoutes.includes(item.permission)
  })
  return res.map(item => {
    // 知识点：对象属性重写，对象的属性是唯一的，不会重名，后面写的属性会覆盖旧属性，必须记住这种写法
    return {
      ...item,
      children: item.children.filter(ele =>
        secondRoutes.includes(ele.permission)
      )
    }
  })
}
